/*
 * shift_reg.c
 *
 *  Created on: Jan 30, 2025
 *      Author: wzx05
 */

#include <Motor/shift_reg.h>
#include <Motor/stepper_cfg.h>

void ShiftReg_Init(const ShiftReg_Handle* hreg) {
    GPIO_InitTypeDef gpio_init = {0};

    // 配置 SER 引脚
    gpio_init.Pin = hreg->ser_pin;
    gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init.Pull = GPIO_NOPULL;
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(hreg->ser_port, &gpio_init);

    // 配置 SRCLK 引脚
    gpio_init.Pin = hreg->srclk_pin;
    HAL_GPIO_Init(hreg->srclk_port, &gpio_init);

    // 配置 RCLK 引脚
    gpio_init.Pin = hreg->rclk_pin;
    HAL_GPIO_Init(hreg->rclk_port, &gpio_init);
}

// 初始化移位寄存器输出状态机（用于实现基于定时器的非阻塞延迟输出，基于定时器频率控制GPIO波形脉宽）
// 并启动一次移位传输用于对齐SER到SHCP的下降沿。传入的 transfer 是由上层管理的状态机实例。
void ShiftReg_StartTransfer(ShiftReg_Transfer* transfer, uint16_t data) {
    //transfer->hreg = hreg;
	const ShiftReg_Handle* hreg = transfer->hreg;

    transfer->data = data;
    transfer->bit_index = 15;  // 从最高位开始

    // 先设置初始 SER（确保在 SRCLK 上升沿前数据已稳定）
    HAL_GPIO_WritePin(hreg->ser_port, hreg->ser_pin,
        ((data >> transfer->bit_index) & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    // 拉低 RCLK，准备移位
    HAL_GPIO_WritePin(hreg->rclk_port, hreg->rclk_pin, GPIO_PIN_RESET);
    // 初始状态：等待上升沿采样当前 SER
    transfer->state = SHIFTREG_STATE_OUTPUT_HIGH;
}

// 状态机推进函数，由定时器中断或调度周期性调用，避免GPIO输出过程产生阻塞
void ShiftReg_TransferHandler(ShiftReg_Transfer* transfer) {
    // 如果当前状态为 IDLE 或 DONE，则不处理
    if (transfer->state == SHIFTREG_STATE_IDLE || transfer->state == SHIFTREG_STATE_DONE) {
        return;
    }

    const ShiftReg_Handle* hreg = transfer->hreg;

    switch(transfer->state) {
        case SHIFTREG_STATE_OUTPUT_HIGH:
            // 拉高 SRCLK：上升沿采样当前 SER
            HAL_GPIO_WritePin(hreg->srclk_port, hreg->srclk_pin, GPIO_PIN_SET);
            // 转换状态，等待下降沿
            transfer->state = SHIFTREG_STATE_OUTPUT_LOW;
            break;

        case SHIFTREG_STATE_OUTPUT_LOW:
            // 拉低 SRCLK，产生下降沿
            HAL_GPIO_WritePin(hreg->srclk_port, hreg->srclk_pin, GPIO_PIN_RESET);
            // 移动到下一位
            transfer->bit_index--;
            if (transfer->bit_index < 0) {
                // 所有位输出完毕，置为 DONE 状态，拉高 RCLK 锁存输出
                transfer->state = SHIFTREG_STATE_DONE;
                HAL_GPIO_WritePin(hreg->rclk_port, hreg->rclk_pin, GPIO_PIN_SET);
                //printf("Shift register transfer completed.\n");
            } else {
                // 更新 SER 到下一个位的值（此时 SRCLK 为低，符合建立时间要求）
                HAL_GPIO_WritePin(hreg->ser_port, hreg->ser_pin,
                    ((transfer->data >> transfer->bit_index) & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET);
                // 切换回 OUTPUT_HIGH 状态，等待下一个上升沿采样新 SER 值
                transfer->state = SHIFTREG_STATE_OUTPUT_HIGH;
            }
            break;

        default:
            break;
    }
}


void ShiftReg_Write16(const ShiftReg_Handle* hreg, uint16_t data) {
    // 拉低 RCLK（锁存时钟）开始移位
    HAL_GPIO_WritePin(hreg->rclk_port, hreg->rclk_pin, GPIO_PIN_RESET);

    // 从最高位到最低位依次写入16位数据
    for (int8_t i = 15; i >= 0; i--) {
        // 设置 SER 引脚数据
        HAL_GPIO_WritePin(hreg->ser_port, hreg->ser_pin,
            ((data >> i) & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET);
        // 产生一次 SRCLK 脉冲
        HAL_GPIO_WritePin(hreg->srclk_port, hreg->srclk_pin, GPIO_PIN_SET);
        HAL_GPIO_WritePin(hreg->srclk_port, hreg->srclk_pin, GPIO_PIN_RESET);
    }

    // 拉高 RCLK 锁存输出
    HAL_GPIO_WritePin(hreg->rclk_port, hreg->rclk_pin, GPIO_PIN_SET);
}

